#ifndef SYMBOL_H
#define SYMBOL_H

#include "visitor.h"
#include <string>
#include <map>
#include <vector>

class Context;
class Opcode;
class Scope;
class CFG;

class Symbol{
private:
    std::string  *name;
public:
    Symbol(const char *sym);
    Symbol(const std::string *sym);
    virtual ~Symbol();

    virtual void             printInfo();
    const std::string&  getName(){return *name;};

    enum { NOLOCAL = -1,NOINDEX = -2 };

    /// get the local slot(varsym,funcsym)
    virtual int getLocal();

    /// get the index in constant pool(constsym)
    virtual int getPoolIndex();

};

class VarSymbol : public Symbol{
protected:
    int     localPos;
public:
    VarSymbol(const std::string *str,int local):Symbol(str),localPos(local){};
    int     getLocal();
    void    printInfo();
};

class ConstantSymbol : public Symbol{
protected:
    /// the symbol's index in constant pool
    const   int index;
public:
    ConstantSymbol(const std::string *str,int i):Symbol(str),index(i){};
    virtual int isInteger(){return 0;};
    virtual int isDouble(){return 0;};
    virtual int isString(){return 0;};

    /// return the the symbol's index in constant pool
    virtual int getPoolIndex(){return index;};
};

class FunctionSymbol : public VarSymbol{
public:
    /// the context of this function
    Context *context;

    // TODO: is there a better place to host the opcodes and cfg?
    /// the rough opcodes, to be converted to mir
    /// mapping from pc to opcode
    std::map<int,Opcode*> ops;
    CFG *cfg;

    FunctionNameExpr *funcName;

    /// the function entry address
    int     entryAddr;

    FunctionSymbol(const std::string* sym,int local):VarSymbol(sym,local),entryAddr(0),cfg(0){};
};

class BlockSymbol : public Symbol{
public:
    Scope   *scope;

    BlockSymbol(const std::string *str,Scope *scp):Symbol(str),scope(scp){};
};

/// the pool collect all kinds of constant symbols
class   ConstantPool{
protected:
    static std::vector<ConstantSymbol*> syms;

    /// the index of new created symbol
    static int index;
public:
    static ConstantSymbol* createInteger(std::string *str);
    static ConstantSymbol* createString(std::string *str);

};

/// scope is attached to each control block(e.g. within if/else,while statement)
class Scope{
private:
    // using multimap implys different type of symbols can share the same name
    std::multimap<std::string,Symbol*>  symbols;

    ///the scope enclosing this
    Scope   *upward;
public:
    Scope(Scope *s);
    ~Scope();

    static  Scope*  curScope;
    void        insertSymbol(Symbol* sym);
    Symbol*     getSymbol(std::string *sym);
    Scope*      getUpward(){ return upward; };
    void        printInfo();
};


class SymbolTable{
private:
    //std::vector<Scope*>  scopes;
public:


    /// insert symbol to current scope
    void        insertSymbol(Symbol* sym);


    // TODO: the ctx is redundant
    Symbol*     getSymbol(std::string *sym,const Context *ctx);

    SymbolTable();
    ~SymbolTable();

    void printInfo();
};

class SymbolEnter : public Visitor{
public:
    void visitVariableExpr(VariableExpr& expr);
    void visitFunctionNameExpr(FunctionNameExpr& expr);
    void visitConstantExpr(ConstantExpr& expr);
    void visitBinaryExpr(BinaryExpr& expr);

    void visitVariableDeclare(VariableDeclareStatement& stmt);
    void visitAssignment(AssignmentStatement& stmt);
    void visitParameterList(ParameterListStatement& stmt);
    void visitExprList(ExprListStatement& stmt);
    void visitFunctionDef(FunctionDefStatement& stmt);
    void visitFunctionCall(FunctionCallStatement& stmt);

    void visitBlock(Block &blk);
    void visitWhileStatement(WhileStatement &stmt);
    void visitIfStatement(IfStatement &stmt);
};

#endif

